package dryven.request.controller.paramtransform.baseimpl;

import java.lang.annotation.Annotation;
import java.util.Locale;
import dryven.request.controller.paramtransform.ActionParameterBindCandidate;
import dryven.request.controller.paramtransform.ActionParameterTransformFeedback;
import dryven.request.controller.paramtransform.ActionParameterTransformationException;
import dryven.request.http.Request;
import dryven.request.http.Response;

public class PrimitiveTypeBinder implements ActionParameterBindCandidate {

	@Override
	public void apply(Class<?> type, Annotation[] annotations,
			Object currentValue, Request reauest, Response response, ActionParameterTransformFeedback feedback) {
		if(!(currentValue instanceof String)) {
			return;
		}
		String value = currentValue.toString();
		feedback.setParameter(convertPrimitiveType(type, value));
	}

	public Object convertPrimitiveType(Class<?> type, String value) {
		try {				
				if(type.equals(Integer.TYPE) || type.equals(Integer.class)) {
					return Integer.parseInt(value);
				} else if(type.equals(Boolean.TYPE) || type.equals(Boolean.class)) {
					if(value.equals("1") || value.toLowerCase(Locale.ENGLISH).equals("true")) {
						return Boolean.TRUE;
					} else if(value.equals("0") || value.toLowerCase(Locale.ENGLISH).equals("false")) {
						return Boolean.FALSE;
					} else {
						throw new ActionParameterTransformationException(String.format("Could not convert boolean parameter. Value given: %s",value));
					}
				} else if(type.equals(Short.TYPE) || type.equals(Short.class)) {
					return Short.parseShort(value);
				} else if(type.equals(Byte.TYPE) || type.equals(Byte.class)) {
					return Byte.parseByte(value);
				} else if(type.equals(Double.TYPE) || type.equals(Double.class)) {
					if(value.trim().equals("2.2250738585072012e-308")) {
						throw new ActionParameterTransformationException("Attempt to exploit the x87 register issue.");
					}
					return Double.parseDouble(value);
				} else if(type.equals(Float.TYPE) || type.equals(Float.class)) {
					return Float.parseFloat(value);
				} else if(type.equals(Long.TYPE) || type.equals(Long.class)) {
					return Long.parseLong(value);
				} else if(type.isEnum()) {
					boolean foundEnumValue = false;
					Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) type;
					Enum<?> selectedEnumValue = null;
					for (Enum<?> enumValue : enumType.getEnumConstants()) {
						if(enumValue.name().equalsIgnoreCase(value) || String.valueOf(enumValue.ordinal()).equals(value)) {
							selectedEnumValue = enumValue;
							foundEnumValue = true;
							break;
						}
					}
					if(!foundEnumValue) {
						throw new ActionParameterTransformationException(String.format("Could not convert enum parameter, given value: %s",value));
					}
					return selectedEnumValue;
				} else if(type.equals(String.class)) {
					return value;
				}
		} catch(Exception e) {
			throw new ActionParameterTransformationException(String.format("Parse error while converting parameter, given value",value),e);
		}
		return null;
	}

	@Override
	public boolean canApply(Class<?> type, Annotation[] annotations) {
		return 
			type.equals(Integer.TYPE) || 
			type.equals(Integer.class) || 
			type.equals(Boolean.TYPE) || 
			type.equals(Boolean.class) ||
			type.equals(Short.TYPE) || 
			type.equals(Short.class) ||
			type.equals(Byte.TYPE) || 
			type.equals(Byte.class) ||
			type.equals(Double.TYPE) || 
			type.equals(Double.class) ||
			type.equals(Float.TYPE) || 
			type.equals(Float.class) ||
			type.equals(Long.TYPE) || 
			type.equals(Long.class) ||
			type.isEnum() ||
			type.equals(String.class);
	}

	@Override
	public boolean isValueGenerator() {
		return false;
	}

	@Override
	public String serializeParameter(Object o) {
		return o.toString();
	}

}
